# -* coding= utf-8 -*-
import pandas as pd
import QUANTAXIS as QA

# ==============================================
# KDJ
# 指标说明：
# KDJ，其综合动量观念、强弱指标及移动平均线的优点，早年应用在期货投资方面，功能颇为显著，目前为股市中最常被使用的指标之一。
# 买卖原则：
# 1.K线由右边向下交叉D值做卖，K线由右边向上交叉D值做买。
# 2.高档连续二次向下交叉确认跌势，低挡连续二次向上交叉确认涨势。
# 3.D值<20%超卖，D值>80%超买，J>100%超买，J<10%超卖。
# 4.KD值于50%左右徘徊或交叉时，无意义。
# 5.投机性太强的个股不适用。
# 6.可观察KD值同股价的背离，以确认高低点。
# ==============================================
# MACD
# 指标说明：
# MACD指数平滑异同移动平均线为两条长、短的平滑平均线。
# 其买卖原则为：
# 1.DIFF、DEA均为正，DIFF向上突破DEA，买入信号参考。
# 2.DIFF、DEA均为负，DIFF向下跌破DEA，卖出信号参考。
# 3.DEA线与K线发生背离，行情可能出现反转信号。
# 4.分析MACD柱状线，由红变绿(正变负)，卖出信号参考；由绿变红，买入信号参考。
# ===============================================
# BIAS
# 指标说明：
# 乖离率表现个股当日收盘价与移动平均线之间的差距。正的乖离率愈大，表示短期获利愈大，则获利回吐的可能性愈高；负的乖离率愈大，则空头回补的可能性愈高。
# 按个股收盘价与不同天数的平均价之间的差距，可绘制不同的BIAS线。
# ===============================================
# CCI
# 指标说明：
# 按市场的通行的标准，CCI指标的运行区间可分为三大类：大于﹢100、小于-100和﹢100——-100之间。　　
# 1.当CCI＞﹢100时，表明股价已经进入非常态区间——超买区间，股价的异动现象应多加关注。　　
# 2.当CCI＜-100时，表明股价已经进入另一个非常态区间——超卖区间，投资者可以逢低吸纳股票。　　
# 3.当CCI介于﹢100——-100之间时表明股价处于窄幅振荡整理的区间——常态区间，投资者应以观望为主。
# =========================================================================
# ADTM
# 原理：
#     1、如果开盘价<=昨日开盘价，DTM=0，如果开盘价>昨日开盘价，DTM=(最高价-开盘价)和(开盘价-昨日开盘价)的较大值。
#     2、如果开盘价>=昨日开盘价，DBM=0，如果开盘价<昨日开盘价，DBM=(开盘价-最低价)和（昨日开盘价-开盘价）的较大值
#     3、STM=DTM在N个周期内的和。
#     4、SBM=DBM在N个周期内的和。
#     5、如果STM>SBM,ADTM=(STM-SBM)/STM,如果STM=SBM,ADTM=0,如果STM<SBM,ADTM=(STM-SBM)/SBM。
#     6、ADTM MA为ADTM在某周期内的简单移动平均。
# 用法：
#     1、该指标在+1到-1之间波动；
#     2、低于-0.5时为很好的买入点,高于+0.5时需注意风险。
from StrategyParameter import Advise, AdviseX, Common


class KLineMa:
    running_time = None
    ma05_trend = None
    ma10_trend = None
    ma20_trend = None
    ma30_trend = None
    ma60_trend = None
    ma120_trend = None
    ma240_trend = None

    last_ma05_trend = None
    last_ma10_trend = None
    last_ma20_trend = None
    last_ma30_trend = None
    last_ma60_trend = None
    last_ma120_trend = None
    last_ma240_trend = None

    ma_distance = None
    ma = None
    last_ma = None
    ma_trend = None
    last_ma_turn = None
    last_ma_turn_5_10 = None
    ma_turn = None
    ma_turn_5_10 = None

    ma05_10 = None

    long_ma_distance = None
    short_ma_distance = None
    short_degree = None

    bar = None

    def analyze(self, bar, market_data, begin_trade_time):
        market_data.index.names = ["datetime", "code"]
        self.bar = bar

        self.last_ma05_trend = self.ma05_trend
        self.last_ma10_trend = self.ma10_trend
        self.last_ma20_trend = self.ma20_trend
        self.last_ma30_trend = self.ma30_trend
        self.last_ma60_trend = self.ma60_trend
        self.last_ma120_trend = self.ma120_trend
        self.last_ma240_trend = self.ma240_trend

        data = market_data[-250:]
        ma, last_ma = self.MA(bar, data)
        self.running_time = bar.running_time
        if self.running_time != begin_trade_time:
            ma = ma[-30:]

        self.ma = ma
        self.last_ma = last_ma
        self.ma05_trend = self.get_trend(bar, ma.MA5)
        self.ma10_trend = self.get_trend(bar, ma.MA10)
        self.ma20_trend = self.get_trend(bar, ma.MA20)
        self.ma30_trend = self.get_trend(bar, ma.MA30)
        self.ma60_trend = self.get_trend(bar, ma.MA60)
        self.ma120_trend = self.get_trend(bar, ma.MA120)
        self.ma240_trend = self.get_trend(bar, ma.MA240)

        tips = ""
        ma_trend_list = [self.ma240_trend.degree, self.ma120_trend.degree, self.ma60_trend.degree, self.ma30_trend.degree,
                         self.ma20_trend.degree, self.ma10_trend.degree, self.ma05_trend.degree]
        tips = " , ".join(pd.Series(ma_trend_list).apply(lambda x: f"{x:.2f}"))

        ma_index = self.ma.index[-1]
        series_turn_index = [["MA5", "MA10", "MA20", "MA30"], [ma_index[1], ma_index[1], ma_index[1], ma_index[1]]]

        ma05_10_list = [ma_trend_list[-1] - ma_trend_list[-2]]
        if self.ma05_10 is None:
            self.ma05_10 = pd.Series(ma05_10_list, index=[ma_index])
        else:
            list = pd.Series(ma05_10_list, index=[ma_index])
            self.ma05_10 = self.ma05_10.append(list)[-4:]

        if self.ma_turn is not None:
            self.last_ma_turn = self.ma_turn

        if Common.has_top_turn(pd.Series(ma_trend_list[-4:], index=series_turn_index)):
            tips += f" MA5~30顶部"
            self.ma_turn = AdviseX.Top_Turn
        elif Common.has_bottom_turn(pd.Series(ma_trend_list[-4:], index=series_turn_index)):
            self.ma_turn = AdviseX.Bottom_Turn
            tips += f" MA5~30底部"
        else:
            self.ma_turn = AdviseX.Unknown

        if self.ma_turn_5_10 is not None:
            self.last_ma_turn_5_10 = self.ma_turn_5_10

        if Common.has_top_turn(self.ma05_10):
            tips += f" MA5-10顶部"
            self.ma_turn_5_10 = AdviseX.Top_Turn
        elif Common.has_bottom_turn(self.ma05_10):
            self.ma_turn_5_10 = AdviseX.Bottom_Turn
            tips += f" MA5-10底部"
        else:
            self.ma_turn_5_10 = AdviseX.Unknown

        tips += f" MA5({self.ma05_trend.last_degree} Δ:{self.ma05_trend.last_degree_rise:.2f})"
        if self.ma05_trend.ma_turn == AdviseX.Top_Turn:
            tips += f"顶部,距离现在{self.ma05_trend.ma_turn_distance}"
        elif self.ma05_trend.ma_turn == AdviseX.Bottom_Turn:
            tips += f"底部,距离现在{self.ma05_trend.ma_turn_distance}"

        self.long_ma_distance = round(self.last_ma[-3:].diff(-1).dropna().mean(), 4)
        self.short_ma_distance = round(self.last_ma[:5].diff(-1).dropna().mean(), 4)
        self.short_degree = round(pd.Series(ma_trend_list[-3:]).mean(), 4)
        tips += f" 长期线差：{self.long_ma_distance} 短期线差：{self.short_ma_distance} 短期角度:{self.short_degree}"
        print(f"{bar.running_time} {tips}")


    @staticmethod
    def get_trend(bar, ma):
        trend = Advise()
        series = ma[- AdviseX.Trend_Length:]
        diff = series.diff().dropna()
        trend.direction = AdviseX.Up \
            if (diff > 0).all() else AdviseX.Down \
            if (diff < 0).all() else AdviseX.Unknown
        trend.degree = series.values[-1] - series.values[0]

        series = ma[- AdviseX.Trend_Length * 2:]
        diff = series.diff()
        top_turn = series.rolling(4).apply(Common.has_top_turn).dropna()
        diff_top_turn = diff.dropna().rolling(3).apply(Common.has_top_turn).dropna()
        bottom_turn = series.rolling(4).apply(Common.has_bottom_turn).dropna()
        diff_bottom_turn = diff.dropna().rolling(3).apply(Common.has_bottom_turn).dropna()

        trend.ma_turn, trend.ma_turn_distance = Common.get_turn(bar, top_turn, bottom_turn)
        trend.diff_turn, trend.diff_turn_distance = Common.get_turn(bar, diff_top_turn, diff_bottom_turn)

        trend.last_degree = round(diff[-1], 4)
        trend.last_degree2 = round(diff[-2], 4)
        trend.last_degree_rise = trend.last_degree - trend.last_degree2
        return trend

    @staticmethod
    def MA(bar, data):
        ma = QA.QA_indicator_MA(data, 5, 10, 20, 30, 60, 120, 240)
        last_ma = pd.Series(
            [ma.MA5[-1], ma.MA10[-1], ma.MA20[-1], ma.MA30[-1], ma.MA60[-1], ma.MA120[-1], ma.MA240[-1]],
            index=["MA5", "MA10", "MA20", "MA30", "MA60", "MA120", "MA240"])
        return ma, last_ma
